#ifndef GTL_ENGINE_EVENT_HEADER_GUARD
#define GTL_ENGINE_EVENT_HEADER_GUARD

#include <GTL/gtlAutomember.h>
#include <GTL/gtlFunctionBinding.h>
#include <GTL/gtlCommon.h>
#include "eastl/string.h"

namespace GTL
{
	namespace Engine
	{
		struct FunctionParamDescriptor
		{
			eastl::vector<int> Types;
			template <typename T> static FunctionParamDescriptor Describe(int(T::*func)()) {
				FunctionParamDescriptor fpd;
				return fpd;
			}
			template <typename T, typename ARG1_TYPE> static FunctionParamDescriptor Describe(int(T::*func)(ARG1_TYPE)) {
				FunctionParamDescriptor fpd;
				fpd.Types.push_back(GTL::Functions::TypeDeducer<ARG1_TYPE>::type);
				return fpd;
			}
			template <typename T, typename ARG1_TYPE, typename ARG2_TYPE> static FunctionParamDescriptor Describe(int(T::*func)(ARG1_TYPE,ARG2_TYPE)) {
				FunctionParamDescriptor fpd;
				fpd.Types.push_back(GTL::Functions::TypeDeducer<ARG1_TYPE>::type);
				fpd.Types.push_back(GTL::Functions::TypeDeducer<ARG2_TYPE>::type);
				return fpd;
			}
			template <typename T, typename ARG1_TYPE, typename ARG2_TYPE, typename ARG3_TYPE> static FunctionParamDescriptor Describe(int(T::*func)(ARG1_TYPE,ARG2_TYPE,ARG3_TYPE)) {
				FunctionParamDescriptor fpd;
				fpd.Types.push_back(GTL::Functions::TypeDeducer<ARG1_TYPE>::type);
				fpd.Types.push_back(GTL::Functions::TypeDeducer<ARG2_TYPE>::type);
				fpd.Types.push_back(GTL::Functions::TypeDeducer<ARG3_TYPE>::type);
				return fpd;
			}
		};

		typedef int(*CallerType)(char*, void*, const GTL::Functions::VariableVariant*, size_t, const char*);

		struct Event : public GTL::AutoMembers::AutoMember
		{
			virtual int GetType() { return 'even'; }

			Event()
			{
				Handler.FunctionPointer = 0;
				AddAutoMemberToPostOrderList();
			}
			virtual ~Event()
			{
				delete[] Handler.FunctionPointer;
			}

			struct EventHandler
			{
				const char* ObjClass;
				const char* FuncName;
				FunctionParamDescriptor Descriptor;
				CallerType CallerFunction;
				char* FunctionPointer;
				void* Object;
			} Handler;
			eastl::string EventName;

			static int CallEvent(EventHandler* handler, const GTL::Functions::VariableVariant* args, size_t argc);
			template <typename EventType, typename T>
			void InitEvent(EventType dummy, T funcptr, const char* name)
			{
				struct EventPointerHolder 
				{ 
					EventType e; 
				}; 
				EventPointerHolder evstr; 
				evstr.e = (EventType)funcptr; 
				char* pointer = new char[sizeof(funcptr)]; 
				memcpy(pointer, &evstr, sizeof(funcptr)); 

				EventName = name;
				void* parentObj = 0;
				if (ParentMember->Is('stat')) /// Event in state
				{
					parentObj = ((StateHolder*)ParentMember)->GetState();
				} else if (ParentMember->Is('root')) /// Event in main object
				{
					parentObj = ParentProducible;
				} else /// Event in other automember (wtf?)
				{
					parentObj = ParentMember;
				}

				EventHandler handler = {ParentProducible->GetName(), EventName.c_str(), FunctionParamDescriptor::Describe(funcptr), 
					GetCaller(funcptr), pointer, parentObj}; 
				Handler = handler; 
			}
		};

		template <typename T>
		T GetCast(const GTL::Functions::VariableVariant& var)
		{
			return (T)var;
		}

		template <typename T>
		static int CallFunc(char* pointer, void* _obj, const GTL::Functions::VariableVariant* args, size_t argc, const char* fname) {
			T* obj = (T*)_obj;
			struct __func_type { typedef int(T::*func_type)(); func_type funcptr; } evstr;
			memcpy(&evstr, pointer, sizeof(__func_type));
			return ((obj)->*(evstr.funcptr))();
		}
		template <typename T, typename ARG1>
		static int CallFunc(char* pointer, void* _obj, const GTL::Functions::VariableVariant* args, size_t argc, const char* fname) {
			GTL_ASSERT(argc >= 1, "Function `%s` requires 1 parameter, %i given", fname, argc);

#ifdef GTL_DEBUG
			if (args[0].type != GTL::Functions::TypeDeducer<ARG1>::type)
				Backend->LogError("Warning! Mismatched types `%s` and `%s` in argument 1 for function `%s`!\n", GTL::Functions::TypeDeducer<ARG1>::name(), "TODO", fname);
#endif

			T* obj = (T*)_obj;
			struct __func_type { typedef int(T::*func_type)(ARG1); func_type funcptr; } evstr;
			memcpy(&evstr, pointer, sizeof(__func_type));
			return ((obj)->*(evstr.funcptr))(GetCast<ARG1>(args[0]));
		}
		template <typename T, typename ARG1, typename ARG2>
		static int CallFunc(char* pointer, void* _obj, const GTL::Functions::VariableVariant* args, size_t argc, const char* fname) {
			GTL_ASSERT(argc >= 2, "Function `%s` requires 2 parameters, %i given", fname, argc);

#ifdef GTL_DEBUG
			if (args[0].type != GTL::Functions::TypeDeducer<ARG1>::type)
				Backend->LogError("Warning! Mismatched types `%s` and `%s` in argument 1 for function `%s`!\n", GTL::Functions::TypeDeducer<ARG1>::name(), "TODO", fname);
			if (args[1].type != GTL::Functions::TypeDeducer<ARG2>::type)
				Backend->LogError("Warning! Mismatched types `%s` and `%s` in argument 2 for function `%s`!\n", GTL::Functions::TypeDeducer<ARG2>::name(), "TODO", fname);
#endif

			T* obj = (T*)_obj;
			struct __func_type { typedef int(T::*func_type)(ARG1,ARG2); func_type funcptr; } evstr;
			memcpy(&evstr, pointer, sizeof(__func_type));
			return ((obj)->*(evstr.funcptr))(GetCast<ARG1>(args[0]), GetCast<ARG2>(args[1]));
		}
		template <typename T, typename ARG1, typename ARG2, typename ARG3>
		static int CallFunc(char* pointer, void* _obj, const GTL::Functions::VariableVariant* args, size_t argc, const char* fname) {
			GTL_ASSERT(argc >= 3, "Function `%s` requires 3 parameters, %i given", fname, argc);

#ifdef GTL_DEBUG
			if (args[0].type != GTL::Functions::TypeDeducer<ARG1>::type)
				Backend->LogError("Mismatched types `%s` and `%s` in argument 1 for function `%s`!\n", GTL::Functions::TypeDeducer<ARG1>::name(), "TODO", fname);
			if (args[1].type != GTL::Functions::TypeDeducer<ARG2>::type)
				Backend->LogError("Mismatched types `%s` and `%s` in argument 2 for function `%s`!\n", GTL::Functions::TypeDeducer<ARG2>::name(), "TODO", fname);
			if (args[2].type != GTL::Functions::TypeDeducer<ARG3>::type)
				Backend->LogError("Mismatched types `%s` and `%s` in argument 3 for function `%s`!\n", GTL::Functions::TypeDeducer<ARG3>::name(), "TODO", fname);
#endif

			T* obj = (T*)_obj;
			struct __func_type { typedef int(T::*func_type)(ARG1,ARG2,ARG3); func_type funcptr; } evstr;
			memcpy(&evstr, pointer, sizeof(__func_type));
			return ((obj)->*(evstr.funcptr))(GetCast<ARG1>(args[0]), GetCast<ARG2>(args[1]), GetCast<ARG3>(args[2]));
		}

		template <typename T> CallerType GetCaller(int(T::*func)()) { return &CallFunc<T>; }
		template <typename T, typename ARG1> CallerType GetCaller(int(T::*func)(ARG1)) { return &CallFunc<T, ARG1>; }
		template <typename T, typename ARG1, typename ARG2> CallerType GetCaller(int(T::*func)(ARG1,ARG2)) { return &CallFunc<T, ARG1, ARG2>; }
		template <typename T, typename ARG1, typename ARG2, typename ARG3> CallerType GetCaller(int(T::*func)(ARG1,ARG2,ARG3)) { return &CallFunc<T, ARG1, ARG2, ARG3>; }

#define event(name, args, ...) \
	struct __class_event_##name : public Event \
	{ \
		self_type* Parent; \
		typedef int(State::*EventType) args; \
		virtual uint32_t OnCreate() { \
			EventType ev_temp = 0; \
			InitEvent(ev_temp, &__proxy_event_##name, #name); \
\
			Parent = (self_type*)ParentProducible; \
			GTL::Common::NOPFunction(&GTL::Common::LValueNOPFunction, (0)); \
 \
			return 0; \
		} \
	} On##name; \
 \
	int __proxy_event_##name args

	}
}


#endif